﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dracham.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Dracham.Components.GameScreens
{
    public class DemoMapScreen : IGameScreen
    {
        private class MapObject
        {
            private Texture2D texture;
            private Point position;
            private Rectangle size;

            internal MapObject(Texture2D texture, Point position, Rectangle size)
            {
                this.texture = texture;
                this.position = position;
                this.size = size;
            }

            internal Boolean IsVisible(Int32 x, Int32 y, Int32 xw, Int32 yw)
            {
                return (
                        (this.position.X >= x || this.position.X + this.size.Width > x) &&
                        (this.position.X <= xw) &&
                        (this.position.Y >= y || this.position.Y + this.size.Height > y) &&
                        (this.position.Y <= yw)
                );
            }

            internal void Render(SpriteBatch spriteBatch, Int32 x, Int32 y)
            {
                spriteBatch.Draw(this.texture, new Rectangle(this.position.X - x, this.position.Y - y, this.size.Width, this.size.Height), this.size, Color.White);
            }
        }

        private class MouseMark
        {
            private Point? src;
            private Point? dst;
            private Texture2D texture;

            public MouseMark()
            {
                this.src = null;
                this.dst = null;
                this.texture = new Texture2D(Dracham.Instance.GraphicsDevice, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color);
                this.texture.SetData<Color>(new Color[] { Color.LightGreen });
            }

            internal void Update(MouseState mouseState)
            {
                if (mouseState.LeftButton == ButtonState.Released)
                {
                    this.src = this.dst = null;
                    return;
                }
                else if (mouseState.LeftButton == ButtonState.Pressed)
                {
                    if (this.src == null)
                        this.src = new Point(mouseState.X, mouseState.Y);

                    this.dst = new Point(mouseState.X, mouseState.Y);
                }
            }

            internal void Draw(SpriteBatch spriteBatch)
            {
                if (this.src != null && this.dst != null)
                {
                    spriteBatch.Draw(this.texture, new Rectangle(this.src.Value.X, this.src.Value.Y, 1, this.dst.Value.Y - this.src.Value.Y), Color.White);
                    spriteBatch.Draw(this.texture, new Rectangle(this.src.Value.X, this.src.Value.Y, this.dst.Value.X - this.src.Value.X, 1), Color.White);
                    spriteBatch.Draw(this.texture, new Rectangle(this.dst.Value.X, this.src.Value.Y, 1, this.dst.Value.Y - this.src.Value.Y), Color.White);
                    spriteBatch.Draw(this.texture, new Rectangle(this.src.Value.X, this.dst.Value.Y, this.dst.Value.X - this.src.Value.X, 1), Color.White);
                }
            }
        }

        private Texture2D background;
        private List<MapObject> mapObjects;
        private Int32 mapWidth = 10000;
        private Int32 mapHeight = 10000;

        private Int32 lastMouseX = -1;
        private Int32 lastMouseY = -1;
        private MouseMark mouseMark = new MouseMark();

        private Int32 x = 0;
        private Int32 y = 0;

        private Int32 xw = 0;
        private Int32 yw = 0;

        public DemoMapScreen()
        {
            this.background = Dracham.Instance.Content.Load<Texture2D>(@"Grass\TGX1");
            this.mapObjects = new List<MapObject>();

            //
            // try add background-objects
            int x = (int)Math.Round(((double)mapWidth) / 320d, 0, MidpointRounding.AwayFromZero) + 1;
            int y = (int)Math.Round(((double)mapHeight) / 240d, 0, MidpointRounding.AwayFromZero) + 1;

            for (Int32 a = 0; a < x; a++)
            {
                for (Int32 b = 0; b < y; b++)
                {
                    this.mapObjects.Add(new MapObject(this.background, new Point(a * 320, b * 240), new Rectangle(0, 0, 320, 240)));
                }
            }

            this.mapObjects.Add(new MapObject(Dracham.Instance.Content.Load<Texture2D>(@"Mountains\TAF0"), new Point(2341, 422), new Rectangle(0, 0, 640, 480)));
            this.mapObjects.Add(new MapObject(Dracham.Instance.Content.Load<Texture2D>("w2fort"), new Point(512, 500), new Rectangle(0, 0, 100, 100)));
        }

        private void UpdateXY()
        {
            this.xw = this.x + Dracham.Instance.GraphicsDevice.Viewport.Width;
            this.yw = this.y + Dracham.Instance.GraphicsDevice.Viewport.Height;
        }

        private List<MapObject> GetObjectsToRender()
        {
            List<MapObject> objects = new List<MapObject>();

            foreach (var mapObject in this.mapObjects)
            {
                if (mapObject.IsVisible(this.x, this.y, this.xw, this.yw))
                    objects.Add(mapObject);
            }

            return objects;
        }

        private Int32 frames = 0;
        public void Draw(Microsoft.Xna.Framework.GameTime gameTime)
        {
            SpriteBatch spriteBatch = Dracham.Instance.SpriteBatch;

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

            //
            // fill background
            //int x = (int)Math.Round(((double)Dracham.Instance.GraphicsDevice.Viewport.Width) / 40d, 0, MidpointRounding.AwayFromZero) + 1;
            //int y = (int)Math.Round(((double)Dracham.Instance.GraphicsDevice.Viewport.Height) / 40d, 0, MidpointRounding.AwayFromZero) + 1;

            //for (int a = 0; a < x; a++)
            //{
            //    for (int b = 0; b < y; b++)
            //    {
            //        spriteBatch.Draw(this.background, new Rectangle(a * 40, b * 40, 40, 40), new Rectangle(280, 81, 40, 40), Color.White);
            //    }
            //}

            var objects = GetObjectsToRender();
            objects.ForEach(o => o.Render(spriteBatch, this.x, this.y));
            this.mouseMark.Draw(spriteBatch);

            spriteBatch.End();

            frames++;
            Dracham.Instance.Window.Title = String.Format("MapObjects: {0}. {1} fps", this.mapObjects.Count, (double)frames / gameTime.ElapsedGameTime.TotalSeconds);
        }

        private void MoveMap()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();

            if (keyboardState.IsKeyDown(Keys.Right))
                this.x += 5;

            if (keyboardState.IsKeyDown(Keys.Left))
                this.x -= 5;

            if (keyboardState.IsKeyDown(Keys.Down))
                this.y += 5;

            if (keyboardState.IsKeyDown(Keys.Up))
                this.y -= 5;

            mouseMark.Update(mouseState);
            if (mouseState.RightButton == ButtonState.Pressed)
            {
                if (lastMouseX == -1 && lastMouseY == -1)
                {
                    lastMouseX = mouseState.X;
                    lastMouseY = mouseState.Y;
                }
                else
                {
                    this.x += (lastMouseX - mouseState.X);
                    this.y += (lastMouseY - mouseState.Y);

                    lastMouseX = mouseState.X;
                    lastMouseY = mouseState.Y;
                }
            }
            else if (mouseState.RightButton == ButtonState.Released)
            {
                lastMouseY = lastMouseX = -1;
            }
        }

        public bool Update(Microsoft.Xna.Framework.GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();

            if (keyboardState.IsKeyDown(Keys.Escape)) return true;

            this.MoveMap();
            this.UpdateXY();

            return false;
        }
    }
}
